﻿#region Using
// System
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// Csla
using Csla;

// Library
using GACAssemblyExporter.Library;
#endregion // Using

namespace GACAssemblyExporter.UI.WinForms
{
    public partial class MainForm : Form
    {
        #region Constructors
        /**********************************************************************
         * Constructors
         *********************************************************************/
        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
            this.InitializeTreeView();
        }// of constructor()
        #endregion // Constructors

        #region Properties
        /**********************************************************************
         * Properties
         *********************************************************************/
        /// <summary>
        /// Gets or sets the assemblies.
        /// </summary>
        /// <value>The assemblies.</value>
        protected IEnumerable<AssemblyInfo> Assemblies { get; private set; }
        #endregion // Properties

        #region Event Methods
        /**********************************************************************
         * Event Methods
         *********************************************************************/
        /// <summary>
        /// Handles the Load event of the MainForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MainForm_Load(object sender, EventArgs e)
        {
            this.AddAssemblyVersionToTitle();
        }// of MainForm_Load(object, EventArgs)

        /// <summary>
        /// Handles the NodeMouseClick event of the tvAssemblies control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeNodeMouseClickEventArgs"/> instance containing the event data.</param>
        private void tvAssemblies_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
        }// of tvAssemblies_NodeMouseClick(object, TreeNodeMouseClickEventArgs)

        /// <summary>
        /// Handles the NodeMouseDoubleClick event of the tvAssemblies control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeNodeMouseClickEventArgs"/> instance containing the event data.</param>
        private void tvAssemblies_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Nodes.Count > 0)
            {
                if (e.Node.IsExpanded)
                {
                    e.Node.Collapse();
                }// if the node is expanded
                else
                {
                    e.Node.Expand();
                }// if the node isn't expanded
            }// if there are sub nodes
        }// of tvAssemblies_NodeMouseDoubleClick(object, TreeNodeMouseClickEventArgs)

        /// <summary>
        /// Handles the AfterCheck event of the tvAssemblies control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeViewEventArgs"/> instance containing the event data.</param>
        private void tvAssemblies_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Tag is AssemblyInfo)
            {
                if (e.Node.Checked) { e.Node.Checked = false; }
            }// if this is an assembly info node
            else if (e.Node.Tag is VersionInfo)
            {
                if (e.Node.Nodes.Count > 0 && e.Node.Checked) { e.Node.Checked = false; }
            }// if this is a version info node
            else if (e.Node.Tag is string)
            {
                if(e.Node.Checked)
                {
                    foreach (TreeNode version in e.Node.Parent.Nodes)
                    {
                        if (string.Compare(version.Tag as string, e.Node.Tag as string, true) != 0)
                        {
                            version.Checked = false;
                        }// if this isn't the selected node
                    }// for each node
                }// if the node is checked
            }// if this is a path info
        }// of tvAssemblies_AfterCheck(object, TreeViewEventArgs)

        /// <summary>
        /// Handles the BeforeCheck event of the tvAssemblies control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.TreeViewCancelEventArgs"/> instance containing the event data.</param>
        private void tvAssemblies_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
        }// of tvAssemblies_BeforeCheck(object, TreeViewCancelEventArgs)

        /// <summary>
        /// Handles the Click event of the bExportSelected control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void bExportSelected_Click(object sender, EventArgs e)
        {
            if(DialogResult.OK.Equals(this.fbdExportDirectory.ShowDialog()))
            {
                this.ExportSelected(this.fbdExportDirectory.SelectedPath);
            }// if the user clicked OK
        }// of bExportSelected_Click(object, EventArgs)
        #endregion // Event Methods

        #region UI Methods
        /**********************************************************************
         * UI Methods
         *********************************************************************/
        /// <summary>
        /// Adds the assembly version to title.
        /// </summary>
        private void AddAssemblyVersionToTitle()
        {
            this.Text = string.Format("{0} v{1}", this.Text, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
        }// of AddAssemblyVersionToTitle()

        /// <summary>
        /// Initializes the tree view.
        /// </summary>
        private void InitializeTreeView()
        {
            this.StartWait();

            try
            {
                this.Assemblies = AssemblyInfoList.Get(ConfigurationManager.AppSettings["RootAssemblyDirectory"] ?? @"c:\windows\assembly").OrderBy<AssemblyInfo, string>(ai => ai.Name);
                foreach (AssemblyInfo ai in this.Assemblies)
                {
                    TreeNode tnAssembly = this.CreateAssemblyTreeNode(ai);
                    if (tnAssembly.Nodes.Count > 0)
                    {
                        this.tvAssemblies.Nodes.Add(tnAssembly);
                    }// if there are versions
                }// for each assebmly
            }// try to load the list
            catch
            {
                throw;
            }// catch any exception
            finally
            {
                this.EndWait();
            }
        }// of InitializeTreeView()

        /// <summary>
        /// Creates the assembly tree node.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns></returns>
        private TreeNode CreateAssemblyTreeNode(AssemblyInfo assembly)
        {
            TreeNode toReturn = new TreeNode(assembly.Name);
            toReturn.Tag = assembly;

            foreach (VersionInfo vi in assembly.VersionList.OrderBy<VersionInfo, string>(vi => vi.Name))
            {
                toReturn.Nodes.Add(this.CreateVersionTreeNode(vi));
            }// for each version info

            toReturn.Collapse();

            return toReturn;
        }// of TreeNode CreateAssemblyTreeNode(AssemblyInfo)

        /// <summary>
        /// Creates the version tree node.
        /// </summary>
        /// <param name="version">The version.</param>
        /// <returns></returns>
        private TreeNode CreateVersionTreeNode(VersionInfo version)
        {
            TreeNode toReturn = new TreeNode(string.Format("{0} ({1})", version.Name, version.AssemblyId));
            toReturn.Tag = version;
            if (version.AssemblyPathList.Count > 1)
            {
                foreach (string path in version.AssemblyPathList.OrderBy<string, string>(path => path))
                {
                    TreeNode tnPath = new TreeNode(path);
                    tnPath.Tag = path;
                    toReturn.Nodes.Add(tnPath);
                }// for each path
            }// if there is more than one assembly path
            return toReturn;
        }// of TreeNode CreateVersionTreeNode(VersionInfo)
        #endregion // UI Methods

        #region Helper Methods
        /**********************************************************************
         * Helper Methods
         *********************************************************************/
        private void StartWait()
        {
            Cursor.Current = Cursors.WaitCursor;
            this.Enabled = false;
        }// of StartWait()

        private void EndWait()
        {
            this.Enabled = true;
            Cursor.Current = Cursors.Default;
        }// of EndWait()

        private void ExportSelected(string path)
        {
            this.StartWait();
            if (Directory.Exists(path))
            {
                ICollection<string> sourceList = this.GetSelectedPaths(this.tvAssemblies.Nodes, new List<string>());
                foreach (string source in sourceList)
                {
                    FileInfo fi = new FileInfo(source);
                    File.Copy(source, Path.Combine(path, fi.Name), true);
                }// for each source
            }// if the output dir exists
            this.EndWait();
        }// of ExportSelected(string)

        private ICollection<string> GetSelectedPaths(TreeNodeCollection nodes, ICollection<string> pathList)
        {
            foreach (TreeNode node in nodes)
            {
                if (node.Checked)
                {
                    if (node.Tag is VersionInfo)
                    {
                        pathList.Add((node.Tag as VersionInfo).AssemblyPathList[0]);
                    }// if the type is a version info
                    else if (node.Tag is string)
                    {
                        pathList.Add(node.Tag as string);
                    }// if the type is a string
                }// if the item is selected
                pathList = this.GetSelectedPaths(node.Nodes, pathList);
            }// for each tree node
            return pathList;
        }// of IEnumerable<string> GetSelectedPaths(TreeNodeCollection nodes, IEnumerable<string>)
        #endregion // Helper Methods
    }// of class MainForm
}// of namespace GACAssemblyExporter.UI.WinForms